package cn.edu.dgut.css.sai.dgutoauth2.oauth;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * 自定义{@link OAuth2UserService}
 *
 * <p>参考文档：</p>
 * <ul>
 *     <li>莞工CAS-参考文档，查看密码cas ：http://doc.dgut.edu.cn/web/#/4?page_id=22</li>
 * </ul>
 *
 * @author sai
 * @see OAuth2UserRequest
 * @see OAuth2AccessToken
 * @since 1.0
 */
final class SaiDGUTOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

    final String USER_INFO_API = "https://cas.dgut.edu.cn/oauth/getUserInfo";
    private final RestTemplate restTemplate;

    public SaiDGUTOAuth2UserService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        String accessToken = userRequest.getAccessToken().getTokenValue();
        String openid = (String) userRequest.getAdditionalParameters().get("openid");
        Map<String, Object> userInfo = getUserInfo(accessToken, openid);
        // 构造 Oauth2User
        return createOAuth2User(userInfo, userRequest);
    }

    private OAuth2User createOAuth2User(Map<String, Object> userInfo, OAuth2UserRequest userRequest) {
        Set<GrantedAuthority> authorities = new LinkedHashSet<>();
        authorities.add(new OAuth2UserAuthority(userInfo));
        OAuth2AccessToken token = userRequest.getAccessToken();
        for (String authority : token.getScopes()) {
            authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
        }
        return new DefaultOAuth2User(authorities, userInfo, "openid");
    }

    private Map<String, Object> getUserInfo(String accessToken, String openid) {
        // @formatter:off
        URI uri = UriComponentsBuilder.fromUriString(USER_INFO_API)
                                        .queryParam("access_token", accessToken)
                                        .queryParam("openid", openid)
                                        .encode()
                                        .build()
                                        .toUri();
        ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange(RequestEntity.post(uri).build(), new ParameterizedTypeReference<>() {});
        // @formatter:on
        Map<String, Object> userInfoResponseBody = responseEntity.getBody();
        Assert.notNull(userInfoResponseBody, "userInfoResponseBody 不能为 null!");
        Assert.isTrue(userInfoResponseBody.get("error") == null, "获取用户基本信息失败!");
        return userInfoResponseBody;
    }
}
